﻿using GALibrary.Entity;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace GALibrary.Scheduling
{
    public abstract class BaseScheduler : ICommon, IScheduling
    {
        protected IScheduling nextLevelSchedule = null;
        protected List<TaskSchedulerGA> tasks = null;
        public void LoadBalance()
        {
            throw new NotImplementedException();
        }

        /// <summary>
        /// For main program
        /// visual timer
        /// </summary>
        /// <param name="_time">visual time</param>
        /// <returns></returns>
        public TaskSchedulerGA StartAtTime(int _time)
        {
            if (this.tasks.Count > 0)
            {
                var tempTask = tasks[0];
                tempTask.ExecutedDuration++;
                this.AddTurnaroundTime(tasks.Where(p => (p.Duration != p.TurnaroundTime && p.TurnaroundTime != 0) || (p.TaskId == tempTask.TaskId)).Select(p => p).ToList());
                this.AddWaitingTime(tasks.Where(p => p.TurnaroundTime == 0 ).Select(p => p).ToList());
                if (tempTask.Duration == tempTask.ExecutedDuration)
                {
                    tasks.Remove(tempTask);
                    if(nextLevelSchedule != null)
                    { 
                        var tempUnderLevelTask = ((BaseScheduler)nextLevelSchedule).GetPeekTasks();
                        if (tempUnderLevelTask != null)
                            tasks.Add(tempUnderLevelTask);
                    }
                }
                return tempTask;
            }
            return null;
        }

        /// <summary>
        /// For main event trigger
        /// </summary>
        /// <param name="task"></param>
        public void AddTask(Entity.TaskSchedulerGA task)
        {
            tasks.Add(task);
            if (nextLevelSchedule != null && tasks.Count > 1)
            {
                var tempUnScheduleTask = this.FindUnScheduleTask();
                this.tasks.Remove(tempUnScheduleTask);
                this.AssignToOther(tempUnScheduleTask);
            }else
            {
                this.OwnSort();
            }
        }
        protected void AddWaitingTime()
        {
            AddWaitingTime(tasks.Where(p => p.TurnaroundTime == 0).Select(p => p).ToList());
        }

        protected void AddWaitingTime(List<TaskSchedulerGA> _tasks)
        {
            foreach (var task in _tasks ?? tasks)
            {
                task.WaitingTime++;
            }
            if (this.nextLevelSchedule != null)
                ((BaseScheduler)nextLevelSchedule).AddWaitingTime();

        }

        protected void AddTurnaroundTime()
        {
            AddTurnaroundTime(tasks.Where(p => (p.Duration != p.ExecutedDuration && p.TurnaroundTime != 0)).Select(p => p).ToList());
        }
        protected void AddTurnaroundTime(List<TaskSchedulerGA> _tasks)
        {
            foreach (var task in _tasks ?? tasks)
            {
                task.TurnaroundTime++;
            }
            if (this.nextLevelSchedule != null)
                ((BaseScheduler)nextLevelSchedule).AddTurnaroundTime();

        }

        private void AssignToOther(Entity.TaskSchedulerGA task)
        {
            if (task != null)
            nextLevelSchedule.AddTask(task);
        }

        public abstract TaskSchedulerGA FindUnScheduleTask();

        public abstract TaskSchedulerGA GetPeekTasks();

        public abstract void OwnSort();
    }
}
